// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Globalization;

namespace Swaggatherer;

internal static class Template
{
    public static string Execute(IReadOnlyList<RouteEntry> entries)
    {
        var controllerCount = 0;
        var templatesVisited = new Dictionary<string, (int ControllerIndex, int ActionIndex)>(
            StringComparer.OrdinalIgnoreCase);

        var setupEndpointsLines = new List<string>();
        for (var i = 0; i < entries.Count; i++)
        {
            var entry = entries[i];

            // In attribute routing, same template is used for all actions within that controller. The following
            // simulates that where we only increment the controller count when a new endpoint for a new template
            // is being created.
            var template = entry.Template.TemplateText;
            if (!templatesVisited.TryGetValue(template, out var visitedTemplateInfo))
            {
                controllerCount++;
                visitedTemplateInfo = (controllerCount, 0);
            }

            // Increment the action count within a controller template
            visitedTemplateInfo.ActionIndex++;
            templatesVisited[template] = visitedTemplateInfo;

            var controllerName = $"Controller{visitedTemplateInfo.ControllerIndex}";
            var actionName = $"Action{visitedTemplateInfo.ActionIndex}";

            var httpMethodText = entry.Method == null ? "httpMethod: null" : $"\"{entry.Method.ToUpperInvariant()}\"";
            setupEndpointsLines.Add($"            Endpoints[{i}] = CreateEndpoint(\"{template}\", \"{controllerName}\", \"{actionName}\", {httpMethodText});");
        }

        var setupRequestsLines = new List<string>();
        for (var i = 0; i < entries.Count; i++)
        {
            var entry = entries[i];
            setupRequestsLines.Add($"            Requests[{i}] = new DefaultHttpContext();");
            setupRequestsLines.Add($"            Requests[{i}].RequestServices = CreateServices();");

            if (entry.Method != null)
            {
                setupRequestsLines.Add($"            Requests[{i}].Request.Method = HttpMethods.GetCanonicalizedValue({entries[i].Method});");
            }

            setupRequestsLines.Add($"            Requests[{i}].Request.Path = \"{entries[i].RequestUrl}\";");
        }

        var setupMatcherLines = new List<string>();
        for (var i = 0; i < entries.Count; i++)
        {
            setupMatcherLines.Add($"            builder.AddEndpoint(Endpoints[{i}]);");
        }

        return string.Format(
            CultureInfo.InvariantCulture,
            @"
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Matching;

namespace Microsoft.AspNetCore.Routing
{{
    // This code was generated by the Swaggatherer
    public partial class GeneratedBenchmark : EndpointRoutingBenchmarkBase
    {{
        private protected const int EndpointCount = {3};

        private protected void SetupEndpoints()
        {{
            Endpoints = new RouteEndpoint[{3}];
{0}
        }}

        private protected void SetupRequests()
        {{
            Requests = new HttpContext[{3}];
{1}
        }}

        private protected Matcher SetupMatcher(MatcherBuilder builder)
        {{
{2}
            return builder.Build();
        }}

        private RouteEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod)
        {{
            var requiredValues = new
            {{
                area = (string)null,
                controller = controllerName,
                action = actionName,
                page = (string)null
            }};
            var defaults = new
            {{
                area = (string)null,
                controller = controllerName,
                action = actionName,
                page = (string)null
            }};

            var metadata = new List<object>();
            if (httpMethod != null)
            {{
                metadata.Add(new HttpMethodMetadata(new string[] {{ httpMethod }}));
            }}

            return CreateEndpoint(
                template,
                defaults: defaults,
                requiredValues: requiredValues,
                metadata: metadata,
                routeName: controllerName);
        }}
    }}
}}",
string.Join(Environment.NewLine, setupEndpointsLines),
string.Join(Environment.NewLine, setupRequestsLines),
string.Join(Environment.NewLine, setupMatcherLines),
entries.Count);
    }
}
